Skip to content

feat(orgtrack): WorkItem + Routine PM protocol v1 (phases 0-8) - #683

Open
Neonforge98 wants to merge 33 commits into
developfrom
feat/orgtrack-pm-protocol
Open

feat(orgtrack): WorkItem + Routine PM protocol v1 (phases 0-8)#683
Neonforge98 wants to merge 33 commits into
developfrom
feat/orgtrack-pm-protocol

Conversation

@Neonforge98

@Neonforge98 Neonforge98 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

orgtrack/v1: WorkItem + Routine PM protocol — phases 0–8 on one branch

Implements the orgtrack/v1 project-management protocol end to end: a frozen wire
contract, a transactional work-item application service, the org2-pm console
CLI, a portable Routine domain driven by a single scheduler, provider bindings,
the Project product-mode axis through session + UI, and the matching cleanup of
the legacy/duplicate paths. 31 commits, rebased on latest develop.

What's in here (by phase)

Phase 0 — frozen contract (docs/orgtrack-pm-protocol/)

  • JSON Schemas for envelope, execution-context, work-item, routine, routine-run
    (+ common defs), validated fixtures (6 success, 18 error), decision log and
    parity matrix. The 18-error table is the canonical error→exit-code map
    (PROJECT_MODE_REQUIRED=5, PERMISSION_DENIED=8, …).

Phase 1 — dead/duplicate product paths deleted

  • work-item-manager builtin agent removed (12→11), dead automation visual
    cluster (−7.6k lines), npm stub. The live Rust automation engine stays.

Phase 2 — work application service (project_management::work_service)

  • Portable FSM (open/in_progress/blocked/completed/failed/cancelled, incl.
    the in_progress→open release edge) with legacy status mapping.
  • Single atomic RMW choke point (BEGIN IMMEDIATE) grows
    AtomicServiceOptions: OCC via expected_local_version
    (PM_ERR:REVISION_CONFLICT), strict-FSM enforcement, operation label +
    reason; audit row (pm_audit_events) and pm_change_seq watermark bump in
    the same transaction.
  • All write paths converged: CreateWorkItemRequest single construction point,
    5 frontend frontmatter-assembly sites removed, standalone whole-row overwrite
    replaced by an atomic closure API.

Phase 3 — org2-pm CLI (separate console binary)

  • New crate orgtrack-pm-cli (bin org2-pm): JSON envelope in/out, 18 wire
    error codes + exit table, context resolver (ORGII_* env +
    .orgii/orgtrack.json manifest), eight work.* commands, idempotency keys
    (pm_idempotency replay/conflict semantics).
  • Mode catalogs converged to one source (ALL_AGENT_EXEC_MODES).

Phases 4–5 — portable Routine domain + unified runtime

  • routine_service: portable spec (deny_unknown_fields rejects
    model/account/workspace leakage), Kahn cycle validation, idempotent
    routine.apply revisions, routine.invoke materializes root+children work
    items through work.create with depends_on/generated_by edges in
    pm_relations; ordered run_status projection.
  • Startup converter turns legacy RoutineDefinitions into portable specs and
    disables converted legacy rows (anti-double-fire); the portable scheduler
    pass is the only cron driver (legacy automation cron path deleted).
  • Orchestrator completion is now an explicit audited policy
    (work.transition, reason "completion policy"), with stale-signal rejection
    (TransitionResult::Ignored when execution_lock.active_session_id
    mismatches).

Phase 6 — provider bindings

  • pm_provider_bindings relation table (dual-write + index + lazy backfill),
    provider capability-profile registry, CLI session-ref namespace validation.

Phase 7 — Project product mode (§5.2 two-axis split)

  • agent_sessions.product_mode column (additive migration), resolver
    (work-item launch → project, routine fire → project, explicit selection
    validated closed-enum), threaded through
    SessionLaunchParams/AgentRunLaunchRequest/session aggregate/zod/Session
    type.
  • ModePill grows a 4th in-session entry Project: selecting it writes
    productMode and derives the exec mode (project→build); Project sessions pin
    the pill display.

Phase 8 — gating + cleanup

  • Deny-delta tool policy: manage_work_item/manage_project mutation surface
    denied unless product_mode == "project" (modes never grant tools).
  • manage_project's duplicate WorkItem CRUD retired onto manage_work_item
    (structured guidance for retired actions), terminology sweep, dead
    workflow-drag signal removed.

Gating hardening (found by a full caller-surface audit + real-machine runs)

  • The launch zod schema accepts productMode; Create-Project-with-AI launches
    as project explicitly (its whole purpose is manage_project, but it has no
    workItemId for the resolver to key on).
  • agent_link_session_to_work_item sets product_mode='project' (same rule as
    the launch resolver), so post-hoc-linked sessions stop getting the
    linked-work-item prompt with the tool denied.
  • Agent Org member sessions inherit project when launched from a work item.
  • The subagent overlay re-applies the parent's product mode next to its exec
    mode — closing a delegation escalation through both the inherited-policy and
    fresh-registry dispatch paths.
  • debug_session_execute_tool now enforces the same with_modes composition
    as the LLM path instead of bypassing policy, and the two E2E probe launches
    pass productMode:"project" (plus the launch helper now maps
    agentExecMode to the wire name mode, which was silently dropped before).
  • System-prompt drift: the channel behavioral rules and task-routing sections
    only advertise the PM tools when the effective (policy-filtered) tool
    surface actually contains them.
  • usePatchSession forwarded every patch field except productMode
    (optimistic write, change detector and RPC object all skipped it), so the
    composer's Project selection persisted only the derived exec mode. The
    in-session ModePill was also mounted hideWhenDefault, hiding the picker
    for Build sessions — the exact state every new session starts in. Both
    fixed.

Verification

  • cargo test -p project_management — 559 tests green.
  • cargo test -p orgtrack-pm-cli — 6 cross-process binary E2E green (full
    agent loop, idempotency replay/conflict, routine lifecycle).
  • Full-workspace cargo check + clippy + tsc green.

Real-machine testing (dev-build exe against the real ~/.orgii home,

computer-use + CDP driven, real LLM runs)

  • Startup/migrations: additive product_mode + pm_* DDL migrated the
    real DB cleanly; the unified routine scheduler starts (30s poll).
  • CLI ↔ GUI cross-process (org2-pm while the GUI runs): work create
    hit the PROJECT_MODE_REQUIRED gate (exit 5-family table respected),
    succeeded with --mode project --actor human:vince; the new row appeared in
    the GUI Work Items list within seconds (pm_change_seq watermark pickup).
    UI status change → CLI work show sees in_progress, revision bump and
    actor-attributed history; illegal open→completed rejected with
    INVALID_TRANSITION exit 4; in_progress only enterable via work claim --session-ref (namespace-validated).
  • Policy ground truth (agent_list_effective_tools_for_session): Build
    session — manage_work_item/manage_project registered but policy-filtered
    out of the prompt surface; after session_patch productMode:"project" both
    appear, exec mode stays build (§5.2 axis separation).
  • LLM-driven manage_work_item (MiniMax M3): Project-mode OS-Agent chat
    created CUF-0003 through the real tool loop; CLI cross-verified
    (created_by: agent, portable open).
  • Create Project with AI (real LLM, background session): created project
    pm-protocol-field-test + work item PMP-0001 — this flow is policy-dead
    without the productMode launch fix.
  • Work item → Start Agent (orchestrator): acquire execution lock, status
    auto-transitions to In Progress, launched session gets
    product_mode=project; on LLM failure the lock is released and Retry is
    offered.
  • Completion policy, end to end on the Anthropic account (Claude
    Sonnet 5)
    : the run also caught a production deadlock — proof-of-work
    collection shells out to git inside the work item's BEGIN IMMEDIATE
    transaction, and a GUI-spawned git hangs on this machine, so the open
    transaction held the projects.db write lock forever (sync-worker gc
    failing, CLI STORE_UNAVAILABLE, execution lock never released, Start
    Agent permanently disabled). Fixed by collecting proof-of-work before the
    transaction opens with a 10s bound and applying it in-memory inside the
    mutator. Re-verified live: collection timed out (the git hang is real),
    the transition then committed in ~20ms — session terminal → audited
    work.transition → status completed, execution lock released.
  • Post-hoc link: agent_link_session_to_work_item on an old Build session
    returned the row with productMode:"project".
  • Portable Routine lifecycle on the real DB (CLI): schema
    deny_unknown_fields rejected a stray field with a precise pointer;
    routine apply (revision + fnv snapshot hash) → routine run materialized
    root PMP-0002 + children PMP-0003/0004 with dependency edges → claim/complete
    all → ordered routine status projection converged running → succeeded.
  • Phase 1 deletions visible: Work Item Manager absent from the agent
    picker and assignee picker (11 builtins).

Project-surface smoke (same rig, after the Phase 7 finish batch):

  • Runs surface: sidebar entry renders the run list; expanding the field-test
    run shows the projection recomputed live (row stored running, expand shows
    succeeded with PMP-0003/0004 completed).
  • Root bootstrap: a Project session with no active WorkItem got its root
    (WI-0001) created and linked the moment an accepted submission landed;
    a submission that failed session init (dead provider) correctly created
    nothing — the boundary really is "accepted", not "sent".
  • Always-visible composer pill confirmed on a fresh Build session; the
    active-WorkItem pill renders (static badge for standalone roots).
  • Track as Project: the menu action flips product mode + derives exec mode;
    the first runs caught a real FK bug — session rows carry scopes with no
    local orgs row (the implicit personal org and cloud:<uuid> sidebar
    scopes), which the standalone store's FK rejects. The bootstrap now falls
    back to the NULL (personal) standalone scope for those; fixed and
    re-verified on-device.

LLM account note: the overnight scenarios ran on MiniMax (the original
Anthropic OAuth token had expired); after the fresh Anthropic account was
added, the LLM-driven scenarios were re-exercised on Claude Sonnet 5,
including the full completion-policy loop above.

Phase 7 finish — the Project session surface (§7.2)

  • Root bootstrap host event: a Project session with no active WorkItem
    creates and links its root when the first non-empty user submission is
    accepted — never on mode switch or empty open, no LLM classification. The
    root body preserves the original request; a sessionRef-derived idempotency
    key makes a create-then-link-failure retry replay the same root.
  • Track this / Convert to Project: agent_track_session_as_project
    switches product mode, derives the exec mode like the composer picker,
    invalidates Plan mode's snapshot/restore state (also on any
    session_patch → project, broadcasting agent:exit_plan_mode when a plan
    approval was pending) and creates-or-replays the root from the recorded
    first input. Session-header menu action next to "Link to Work Item".
  • Active WorkItem indicator: Project sessions show a shortId + live-status
    pill in the session context bar (now actually mounted above the transcript);
    click opens the real Work Item panel.
  • Runs surface: routine_service::list_runs +
    project_list_routine_runs / project_routine_run_status commands expose
    pm_routine_runs; a Runs entry in the sidebar BROWSE section opens an
    expandable run list showing each run's generated WorkItems and the
    recomputed ordered status projection.

Follow-ups (intentionally not in this PR)

  • Rendered E2E for the new surfaces (empty/loading/error states).
  • Gated deletions awaiting their frozen preconditions: legacy RoutineFire dual
    path + legacy routine UI, WorkItemSchedule.cron field + migration
    (existing-store migration gate), SyncAdapter → PlanningAdapter rename, repo
    split (dogfood gate).

Phase 0 of the WorkItem+Routine CLI protocol: JSON Schemas for the
envelope, ExecutionContext, WorkItem, Routine and RoutineRun; success
fixtures per command family plus one golden fixture per stable error
code (18); decisions.md freezing mode/capability matrix, error-to-exit
mapping, CLI carrier (separate console binary org2-pm aliased as org2),
provider id namespace, hook naming, workspace manifest, ORGII_* env
names and the pm_change_seq cross-process wake protocol; entry-point
parity matrix scaffold. Fixtures are ajv-validated against the schemas.

Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 1 of the Orgtrack PM protocol migration.

Work Item Manager persona removed: builtin definition + prompt,
registration (12 -> 11 builtin agents), the prompt.rs special case (the
linked-work-item context now keys on session agent_role "custom" +
work_item_id instead of agent identity), Create-with-AI default agent
falls back to builtin:os, hero copy, docs, and E2E specs now exercise
the same scenarios through the generic OS agent.

Dead visual-automation surface removed after call-chain tracing: the
AgentOrgs workflow editor cluster (AutomationWorkflowStep, editor
content/nodes, EditPanel, DnD hooks, action catalog, flattenWorkflow),
the shadowed AgentOrgs/types directory, AutomationTriggerConfig wizard
variant, RulesMemoryEvolution automation types/detail views (page is
markdown-rules-only now), the six dormant agent_automation_* Tauri
commands plus their handler registrations and companion-md generation,
and the never-functional packages/orgtrack npm stub. The Rust
automation engine itself stays - it is live via the channel-enable
path; its cron-path duplication is Phase 5 scope. The read-only
workflowDragActiveAtom in GlobalDragDrop is recorded as Phase 8
cleanup.

manage_work_item / manage_project are frozen (no new behavior) until
their callers migrate to the unified work application service.

Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 2a of the Orgtrack PM protocol migration.

New work_service module in project_management: the portable WorkItem
state machine (open/in_progress/blocked/completed/failed/cancelled)
with a legacy-status mapping and the full transition matrix from the
frozen v1 contract, including the in_progress->open release edge.

The atomic RMW choke point now threads AtomicServiceOptions: optimistic
concurrency via expected_revision against local_version (typed
PM_ERR:REVISION_CONFLICT sentinel), strict-vs-flag-only FSM validation
on status changes, and an audit label/reason. Every mutation - UI
patch, agent tool, sync merge, future CLI - commits an append-only
pm_audit_events row and bumps the single-row pm_change_seq cross-
process watermark in the same transaction (new DDL, plus
pm_idempotency for the Phase 3 wire contract).

acquire/release_execution_lock audit as work.claim / work.release -
the local execution lock is the claim record the portable work.claim
operation absorbs. New project_transition_work_item Tauri command
exposes the strict service path; legacy paths stay fail-open with
violations flagged in the audit stream until Phase 7 flips them.

12 new unit/integration tests; full crate suite 542 passing.

Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 2b (step 1) of the Orgtrack PM protocol migration.

New canonical work.create application operation: a typed
CreateWorkItemRequest DTO with a single Rust construction site for
WorkItemFrontmatter, exposed as project_create_work_item /
work_item_create_standalone and audited (work.create + pm_change_seq).
Short-id allocation stays caller-side because collab orgs mint ids on
the server.

All five TS frontmatter-literal construction sites are gone:
useWorkItemActions, createWorkItemFromDraft, useAiWorkItemCreator and
ProjectPanelView kanban-add now call work.create; the AI creator's
post-launch linked-session write, StandaloneWorkItemDetailPage and
WorkItemPanelView switch from client-side merge + whole-row
writeStandaloneWorkItem (lost-update race) to the atomic partial-update
command. The whole-row write commands remain for sync/merge internals
and E2E seeding only.

Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 2b (step 2): the two agent-tool WorkItemFrontmatter construction
sites (tool_infra create_work_item and manage_work_item standalone
create) now build a CreateWorkItemRequest and call the audited
work.create service handlers instead of assembling 32-field frontmatter
literals and invoking the whole-row write. CreateWorkItemRequest gains
the starred field the agent surface supports.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 2b (step 3): new public update_standalone_work_item_atomic
closure API on the atomic choke point (same IMMEDIATE-transaction,
history, audit, watermark and collab-bridge semantics as the partial
path). The four manage_work_item standalone sites - update, delete,
link_session, unlink_session - stop doing client-side read-modify-write
followed by a whole-row write and run their existing mutation logic
inside the transaction instead.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 3 (vertical slice 1) of the Orgtrack PM protocol migration.

New orgtrack-pm-cli crate producing the org2-pm console binary (PATH-
aliased as org2 by the distribution; the GUI binary stays untouched per
frozen decision 4). Implements the orgtrack/v1 wire contract:

- success/error envelopes with the 18 stable error codes and the frozen
  error-to-exit table (PROJECT_MODE_REQUIRED=5, PERMISSION_DENIED=8);
- context resolver: flags -> ORGII_* env -> .orgii/orgtrack.json
  manifest, mode default build, frozen mode-capability matrix (only
  project exposes the 12-capability mutation surface);
- the eight work commands: list (--ready = portable open + unclaimed),
  show (revision + relations), create/update via the service, claim
  (execution-lock CAS composed with the strict open->in_progress
  transition, lock rolled back on rejection), transition (portable
  states only; in_progress is claim-only), note (append-only comment,
  audited as work.note), relate (typed edge in the new pm_relations
  table);
- PM_ERR sentinel mapping onto stable wire codes; idempotent schema
  init on the canonical store path for entry-point init parity.

work_service grows read_project_work_item_revision, note/relate
handlers and the pm_relations DDL. Cross-process E2E drives the real
binary against a sandbox store: external shell agent completes a work
item end to end, pm_change_seq watermark advances per mutation, audit
stream carries work.claim/note/relate/transition.

Residuals documented in-code: dependsOn joins --ready with Phase 4,
single-transaction claim replaces the compose when the claim handler
lands, --idempotency-key dedup is the next slice.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 3 (slice 2): work_service::run_idempotent guards operations by
(actor, operation, scope, key) against the pm_idempotency table - an
exact replay returns the stored response without re-executing, a
different canonical request under the same key returns
IDEMPOTENCY_CONFLICT (exit 4). The CLI create/claim/transition commands
run their cores through the guard when --idempotency-key is supplied.
E2E covers replayed claim (a re-run would trip INVALID_TRANSITION) and
the conflict path. Post-commit record residual documented in-code.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 3 (slice 3): the mode enum had four divergent enumerations (Rust
AgentExecMode, the caller-less agent_list_modes command returning a
4-entry catalog, the TS AGENT_EXEC_MODES picker, and MODE_LABELS with 5
entries missing wingman). agent_list_modes and its whole TS chain
(rpc procedure, schema, wrapper, AgentExecModeConfig type) are deleted;
MODE_LABELS is now derived from ALL_AGENT_EXEC_MODES so it can never
drift again. AGENT_EXEC_MODES in sessionCreatorConfig.ts remains the
single user-facing picker list, as a deliberate curated subset of the
canonical wire-value set. Prerequisite for adding the product-mode axis
without minting a fifth list.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 1) of the Orgtrack PM protocol migration.

New routine_service module: the portable Routine spec types mirror the
frozen routine.schema.json exactly (deny_unknown_fields makes model/
account/workspace/credential fields unparseable - the boundary the
legacy RoutineDefinition violated), with graph validation (step-id
shape/uniqueness, needs referencing, Kahn acyclicity, ${...} mapping
expressions resolving only to declared inputs/outputs of declared
dependencies, 5-field cron + timezone) and deterministic
canonicalization for the immutable snapshot hash.

routine.apply lands on the new pm_routines table: same canonical body
keeps the revision (idempotent), changed body bumps it, historic runs
untouched, audited + watermarked in the same transaction.
pm_routine_runs DDL ships alongside for the invoke slice. The frozen
Phase 0 fixture parses and validates through the real types - the
schema/fixture/implementation triangle closes in CI.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 2): routine.invoke snapshots the current revision into
an immutable pm_routine_runs row, validates inputs against the
snapshot contract (typed ROUTINE_INPUTS_INVALID sentinel), and
materializes the graph through the canonical work.create handler: one
root WorkItem from the substituted template, one generated child per
step (instruction + input mappings + actor requirement rendered into
the body), parented to the root. Dependencies and run provenance are
durable typed relations (depends_on work://<scope>/<id>, generated_by
run://<id>) in pm_relations - not a singleton routine_source. Manual
and scheduler invocations share this single entry point. 12 service
tests green; full crate suite passing.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 3): the org2 CLI gains the routine namespace -
list/validate/apply/run/status/enable/disable per the frozen contract
(cancel returns UNSUPPORTED_CAPABILITY until the Phase 5
cancel_requested machinery). Spec files parse as YAML or JSON through
one parser; validation failures surface as INVALID_ARGUMENT with the
structured violation list in details. routine run collects repeated
--input k=v pairs.

routine_service grows list/set_enabled and run_status: the durable view
joins pm_routine_runs with the generated items (reverse generated_by
lookup) and recomputes the overall status with the ordered projection
from design section 11, including dependency-aware readiness through
the depends_on relations. E2E: full lifecycle through the real binary -
apply idempotency, materialized 3-step graph, claim + complete a step,
projection stays running as downstream becomes ready.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 4, completing the phase): one-way additive converter
from legacy RoutineDefinition rows to portable specs. CreateWorkItem
and DirectSession modes become single-step routines (prompt = step
instruction) with the legacy concurrency/catch-up policies mapped onto
the portable enums; model/account/harness/workspace/agent targets are
reported as execution-binding warnings for operator re-setup - the
boundary the portable model enforces. UpdateExistingWorkItem routines
are reported as skipped (portable --root-work runs land with Phase 5),
and one-shot triggers convert to manual with the timestamp noted.
Legacy rows stay untouched until the Phase 5 scheduler unification, so
nothing can double-fire. convert_all() produces the written report the
migration plan requires.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 5 (slice 1) of the Orgtrack PM protocol migration.

The routine scheduler tick gains a portable pass: pm_routines schedule
activations fire through the canonical routine.invoke - the same entry
manual CLI runs use - with per-candidate watermarks, croner-evaluated
due windows (UTC for now; the declared timezone rides in the spec), and
the frozen concurrency semantics: an active run suppresses the fire
with an audited routine.fire_suppressed event (skip/coalesce; queue
suppresses too until pending-run dequeue lands), and a missing scope
binding is audited rather than silently dropped.

Startup now converts legacy RoutineDefinitions into portable specs and
DISABLES the converted legacy rows in the same pass, so each routine is
only ever driven by one scheduler - no double-fire window. The written
conversion report lands at ~/.orgii/routine-conversion-report.json.
CreateWorkItem routines carry their project slug over as the
host-local default_scope binding (never part of the portable hash).

A cross-process watermark poller notices pm_change_seq bumps from
external writers (the org2 CLI) and emits the data-changed event so the
UI refreshes (design 13.0). project_management suite: 555 green.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 5 (slice 2): the automation engine was the third parallel cron
executor (routine scheduler, work-item scheduler, automation timer).
Cron-triggered AutomationRules still load - no persisted-data break -
but no longer spawn an execution loop; the audit-visible warn points at
Routines as the recurring-execution home. The dead spawn_cron loop is
deleted. Timer/scheduled-time/file-watch/webhook/channel triggers are
untouched (they are event triggers, not recurring execution).

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 5 (slice 3, completing the phase): the session-terminal ->
work-item completion chain is no longer a silent side effect. Every
orchestrator transition through state_machine::mutate_work_item is now
audited as an explicit work.transition carrying the reason "completion
policy: orchestrator session terminal" - the durable form of the
default completion policy from the design (§17), ready to grow an
org-level off switch.

Stale-signal rejection (§12.4): a terminal event from a session that no
longer holds the item's execution claim returns the new
TransitionResult::Ignored instead of completing a newer episode - late
signals from superseded sessions cannot close current work.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 6 of the Orgtrack PM protocol migration.

ProviderBinding leaves the extras_json blob: pm_provider_bindings is a
relational table keyed (work_item_id, provider) with an index on
(provider, external_id). apply_remote_merge dual-writes it in the same
transaction as the legacy blob (identity can never split-brain), and
find_by_external_ref takes the indexed path first, lazily backfilling
pre-migration bindings it still finds via the legacy scan - the
documented linear-scan pressure point is gone for synced projects.

provider_host grounds the section-15 capability profiles in code: the
bundled org2 provider (execution + provenance), the sync framework
adapters surfaced as planning-profile providers (SyncAdapter/
AdapterDescriptor ARE the planning host interfaces; Phase 8 aligns the
names), and the canonical external CLI providers as reference-only
provenance. validate_session_ref enforces the frozen namespace at the
CLI boundary: claim --session-ref and relate session:// targets reject
hook short names (claude) with a pointer at the canonical id
(claude_code). CLI E2E covers the rejection; 559 crate tests green.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 1) of the Orgtrack PM protocol migration.

agent_sessions gains a product_mode column (build|plan|ask|project,
NULL = build) via the established try_migrate ALTER path - the single
source of truth for persistent WorkItem/Routine mutation intent,
distinct from the runtime exec mode (AgentExecMode) per the frozen
5.2 split. The upsert preserves it against background refreshes
(same posture as agent_exec_mode); update_product_mode is the explicit
write path.

The frozen resolver precedence is wired server-side: launching from a
WorkItem forces project (rule 1), non-interactive routine invokes are
project context (5.1), the explicit launch-time choice is validated
against the closed enum, everything else stays NULL/build. Wire:
SessionLaunchParams.productMode rides next to mode; AgentRunLaunchRequest
carries it through every constructor.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 8 (slice 1): workflowDragActiveAtom could only be set by the
visual workflow editor deleted in Phase 1 - since then it was a
permanently-false input to GlobalDragDrop internal-drag detection. The
atom, its store file, the ref plumbing through useGlobalDragDrop /
useBrowserDragDrop and the isInternalDrag parameter are gone.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 8 (slice 2): the last code-side reference to the retired persona
id is reworded; git grep for work-item-manager / the deleted workflow
editor symbols / the drag atom now returns zero hits outside docs and
locale files. Full suites green: project_management 559,
orgtrack-pm-cli 6 (incl. cross-process binary E2E).

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 8 (slice 3): manage_project loses its duplicate work-item CRUD
surface (list_items/read_item/create_item/update_item/delete_item) -
the audit finding that deleting manage_work_item alone would not
consolidate. The retired actions return structured guidance pointing at
the manage_work_item equivalent instead of a trajectory-visible
execution error, so agents self-correct in one step. manage_project
keeps what manage_work_item does not have: project CRUD, members,
global find, and start_item (orchestrator launch). The work-item param
helpers - including the cron schedule entry point retired in Phase 5 -
leave with the surface; the schema and LLM descriptions shrink to
match.

Pre-commit hook ran. Total eslint: 19, total circular: 0
The debug/test HTTP launch paths and the benchmark launcher construct
SessionLaunchParams literally; they pass product_mode: None (= build)
like every other default caller. Restores the full-workspace build
after the Phase 7 product-mode axis landed.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 2): the productMode data path is now complete in both
directions. Read: SessionAggregateRecord carries the column through
every conversion site (CLI/imported sessions report None = build), the
zod schema and the frontend Session type mirror it camelCase. Write:
session_patch accepts productMode for agent sessions only, validated
against the closed build|plan|ask|project enum on the Rust side so a
typo can never silently grant or drop the Project mutation surface.
The UI layer (ModePill entry, Project panel, Runs view) can now read
session.productMode and patch it through the existing channel.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 3): the one user-visible mode selector gains Project as
its fourth entry, per the frozen 5.2 split - the selector writes the
PRODUCT mode and the runtime exec mode is derived (project -> build,
identity otherwise). In-session selection patches both fields through
the session aggregate; a Project session pins the pill to Project
regardless of the derived exec mode, exits by picking Build/Plan/Ask
(work items are never deleted or completed by leaving - only the
mutation surface stops being exposed), and the reset-on-click shortcut
is disabled so a stray click cannot silently drop Project mode. The
creator/controlled pill variants stay exec-only until the Project
bootstrap flow lands there. COMPOSER_MODES extends the canonical list
- no fifth divergent catalog.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 4): the frozen 5.1 capability boundary is now enforced
at the tool-policy layer. ResolvedToolPolicy::with_modes composes the
exec-mode overlay with a product-mode deny-delta: sessions whose
product_mode is not project (build/plan/ask or unset) subtract
manage_work_item and manage_project from whatever the agent definition
allows. Deny-delta preserved - Project never grants tools beyond the
definition, so switching modes cannot escalate an actor. Both
composition consumers switch to with_modes: the per-turn executor
(reading the session row like the prompt path does) and the
effective-tools RPC. Work-item/routine-launched sessions resolve to
project server-side, so orchestrator flows and Create-with-AI keep
their tools; ordinary Build chat now follows the design - switch to
Project to manage the persistent work graph.

Pre-commit hook ran. Total eslint: 19, total circular: 0
The deny-delta gate on manage_work_item/manage_project left several
launch paths without a way to reach product_mode=project:

- SessionLaunchParams zod schema now accepts productMode, and the
  Create-Project-with-AI creator passes project explicitly (the flow's
  whole purpose is manage_project, but it launches without a workItemId
  so the resolver defaulted it to build).
- agent_link_session_to_work_item now sets product_mode='project' —
  the same rule the launch resolver applies — so post-hoc-linked
  sessions stop getting the linked-work-item prompt with the tool
  denied.
- Agent Org member sessions inherit project when launched from a work
  item (previously only the coordinator got it).
- The subagent overlay re-applies the parent's product mode next to its
  exec mode, closing the delegation escalation through both the
  inherited-policy and fresh-registry dispatch paths.
- debug_session_execute_tool now enforces the same with_modes
  composition as the LLM path instead of bypassing policy.
- The channel behavioral rules and task-routing prompt sections only
  advertise the PM tools when the effective (policy-filtered) tool
  surface actually contains them.
- E2E: the launchSession helper maps agentExecMode to the wire name
  'mode' (previously silently dropped), and the two work-item-manager
  probes launch with productMode=project so they exercise the gated
  path.

Pre-commit hook ran. Total eslint: 19, total circular: 0
Real-machine testing caught two gaps in the Phase 7 UI slice:

- usePatchSession dropped productMode on the wire: the optimistic
  write, the change detector and the RPC patch object all skipped the
  field, so picking Project in the composer only persisted the derived
  exec mode. Forward it through all three.
- The in-session ModePill was mounted with hideWhenDefault, which
  hides the pill for Build sessions — making the Project entry
  unreachable from exactly the state every new session starts in.
  Mount it always-visible in-session.

Also give org2-pm a --help response that lists the command surface
instead of 'Flag --help requires a value'.

Pre-commit hook ran. Total eslint: 19, total circular: 0
The remaining §7.2 product surfaces:

- Host-side root bootstrap: a Project session with no active WorkItem
  creates and links its root when the first non-empty user submission
  is accepted (never on mode switch or empty open), body preserving the
  original request, under a sessionRef-derived idempotency key so a
  create-then-link-failure retry replays the same root instead of
  duplicating it.
- Track this / Convert to Project: new agent_track_session_as_project
  command switches the session to product_mode=project, derives the
  exec mode the way the composer picker does, invalidates Plan mode's
  snapshot/restore state (also done when session_patch flips a session
  to project) and creates-or-replays the root from the recorded first
  input. Surfaced as a session-header menu action next to Link Work
  Item.
- Active WorkItem indicator: Project sessions show a shortId+status
  pill in the session context bar (now actually mounted above the
  transcript); clicking opens the real Work Item panel. Standalone
  bootstrap roots render as a static badge.
- Runs surface: routine_service::list_runs + project_list_routine_runs
  / project_routine_run_status commands expose pm_routine_runs, with a
  Runs entry in the sidebar BROWSE section and an expandable run list
  showing each run's generated WorkItems and recomputed ordered status
  projection.

Pre-commit hook ran. Total eslint: 19, total circular: 0
On-device Track-this runs hit a FOREIGN KEY failure: session rows
carry scopes with no local orgs row — the implicit personal-org and
cloud:<uuid> sidebar scopes — and the standalone store's org FK rejects
both. The bootstrap now keeps the org only when it exists in the local
orgs table and otherwise falls back to the NULL (personal) standalone
scope, the same normalization WorkItemTool applies.

Residual (documented): standalone short ids are per-scope, so a root
created under the personal fallback can share a short id with an
org-scoped item; each session resolves its link through its own org
context.

Pre-commit hook ran. Total eslint: 19, total circular: 0
@Neonforge98
Neonforge98 force-pushed the feat/orgtrack-pm-protocol branch from b3d4049 to 993bad1 Compare August 5, 2026 18:48
On-device the completion policy never landed: proof-of-work collection
shells out to git (rev-parse, default-branch probe, diff stats) and ran
INSIDE the work item's BEGIN IMMEDIATE transaction. On this machine a
GUI-spawned git hangs (the same commands finish in ~1s from a shell),
so the open transaction held the projects.db write lock indefinitely —
starving the sync worker's gc, the org2-pm CLI (STORE_UNAVAILABLE) and
every later completion attempt, while the execution lock stayed claimed
and Start Agent stayed disabled.

Split collect_proof_of_work into a subprocess-side collector that runs
BEFORE the transaction opens and a pure in-memory apply step inside the
mutator, and bound the collector with a 10s timeout so a sick git
degrades to completing without diff stats instead of stalling the
policy. Verified on-device: the timeout fired, the transition then
committed in ~20ms (session terminal -> audited work.transition ->
status completed, execution lock released).

Pre-commit hook ran. Total eslint: 19, total circular: 0
Sending the Launchpad's AI work-item creator bounced the user back to
the start page's Session tab with a cleared composer — the created item
and its running agent were invisible until a toast minutes later. The
post-launch handler reset createTarget to AGENT_SESSION, which the
start page maps to the Session tab.

- The AI work-item flow now opens/focuses the launched session's chat
  tab and makes it the active session; the active-WorkItem pill in the
  session context bar carries the created item.
- Create-Project-with-AI gets the same treatment (it previously stayed
  on a blank creator with only the delayed toast).
- The background-completion toast's copy says 'Open the Session' — it
  now has an actual Open Session action instead of being a dead end.

Pre-commit hook ran. Total eslint: 19, total circular: 0
The draft-fill session launched whatever agent the composer had
selected — usually SDE, whose definition does not register
manage_work_item at all (the Work Item Manager persona retirement
assumed builtin:os would carry the flow, but nothing forced it). The
model then tool-searched, reported the tool missing and finished
without ever filling the draft.

The work-item launch context now carries an agentDefinitionId override
(builtin:os) for the fill session; the item's assignee is untouched.

Also stop linked sessions from narrating plumbing at the user
('This session is already linked to Work Item WI-0003…') — the
linked-work-item prompt block now says to apply the linkage silently.

Pre-commit hook ran. Total eslint: 20, total circular: 0
A follow-up like 'write me another work item' in a linked session
overwrote the linked item's title and body wholesale (observed
on-device: WI-0005 went from an alphabet-song request to a fabricated
sample bug). The linked-work-item prompt block now scopes the
update-not-duplicate rule to iterations on the ORIGINAL request and
directs new/additional item requests to manage_work_item(create_item),
matching the reference semantics in Multica where agent creates are
always new issues (with a server-side duplicate guard) and existing
issues are never repurposed.

Pre-commit hook ran. Total eslint: 18, total circular: 0
@Neonforge98
Neonforge98 force-pushed the feat/orgtrack-pm-protocol branch from 4fc771b to 9402844 Compare August 6, 2026 06:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant